#region Copyright (c) 2006-2010 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2010 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Collections;
using System.Xml;
using System.ComponentModel;
using Widgetsphere.Generator.Common.Util;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.Generator.ProjectItemGenerators;
using Widgetsphere.Generator.Design.Converters;

namespace Widgetsphere.Generator.Models
{
	public enum IdentityTypeConstants
	{
		None,
		Database,
		Code,
	}

	public class Column : ColumnBase, ICodeFacadeObject
	{
		#region Member Variables

		protected const bool _def_primaryKey = false;
		protected const IdentityTypeConstants _def_identity = IdentityTypeConstants.None;
		protected const bool _def_codeImplementedIdentity = false;
		protected const int _def_sortOrder = 0;
		protected const bool _def_gridVisible = false;
		protected const string _def_mask = "";
		protected const double _def_min = double.NaN;
		protected const double _def_max = double.NaN;
		protected const bool _def_isSearchable = false;
		protected const bool _def_isIndexed = false;
		protected const bool _def_isUnique = false;
		protected const bool _def_ComputedColumn = false;
		protected const string _def_Formula = "";

		protected string _codeFacade = "";
		protected bool _primaryKey = _def_primaryKey;
		protected IdentityTypeConstants _identity = _def_identity;
		protected string _default = "";
		protected Reference _parentTableRef = null;
		protected Reference _relationshipRef = null;
		private string _enumType = "";
		private string _friendlyName = "";
		private int _sortOrder = _def_sortOrder;
		private bool _gridVisible = _def_gridVisible;
		private string _mask = _def_mask;
		private double _min = _def_min;
		private double _max = _def_max;
		private bool _isSearchable = _def_isSearchable;
		private bool _isIndexed = _def_isIndexed;
		private DateTime _createdDate = DateTime.Now;
		protected bool _isUnique = _def_isUnique;
		protected string _collate = "";
		protected bool _computedColumn = _def_ComputedColumn;
		protected string _formula = _def_Formula;
		#endregion

		#region Constructor

		public Column(IModelObject root)
			: base(root)
		{
		}

		#endregion

		#region Property Implementations

		public override int Length
		{
			get
			{
				if (this.ComputedColumn) return _def_length;
				return base.Length;
			}
			set
			{
				base.Length = value;
			}
		}

		public override bool AllowNull
		{
			get
			{
				if (this.ComputedColumn) return true;
				else return base.AllowNull && !this.PrimaryKey;
			}
			set { base.AllowNull = value; }
		}

		[
		Browsable(true),
		Description("Determines if this column is used in update or insert statments. Can be used to support calculated fields."),
		Category("Data"),
		DefaultValue(_def_ComputedColumn),
		]
		public bool ComputedColumn
		{
			get { return _computedColumn; }
			set
			{
				_computedColumn = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("computedColumn"));
			}
		}

		[
		Browsable(true),
		Description("Formula for the computed column. This is only considered when the computed column is set to true."),
		Category("Data"),
		DefaultValue(""),
		]
		public string Formula
		{
			get { return _formula; }
			set
			{
				_formula = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("formula"));
			}
		}

		[
		Browsable(true),
		Description("Determine if this column is the table primary key."),
		Category("Data"),
		DefaultValue(_def_primaryKey),
		]
		public bool PrimaryKey
		{
			get
			{
				if (this.ComputedColumn) return false;
				else return _primaryKey;
			}
			set
			{
				_primaryKey = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("primaryKey"));
			}
		}

		[
		Browsable(true),
		Description("Determines the type of identity for this column."),
		Category("Data"),
		DefaultValue(_def_identity),
		]
		public IdentityTypeConstants Identity
		{
			get
			{
				if (this.ComputedColumn) return IdentityTypeConstants.None;
				else return _identity;
			}
			set
			{
				_identity = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("Identity"));
			}
		}

		[Browsable(false)]
		public Reference RelationshipRef
		{
			get { return _relationshipRef; }
			set { _relationshipRef = value; }
		}

		[
		Browsable(true),
		Description("Determines the default value of this column."),
		Category("Data"),
		DefaultValue(""),
		]
		public string Default
		{
			get
			{
				if (this.ComputedColumn) return "";
				else return _default;
			}
			set
			{
				_default = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("Default"));
			}
		}

		[
		Browsable(true),
		Description("Determines the minimum value for a int, long, float value."),
		Category("Data"),
		DefaultValue(_def_min),
		TypeConverter(typeof(RangeMinConverter)),
		]
		public double Min
		{
			get
			{
				if (this.ComputedColumn) return _def_min;
				else return _min;
			}
			set
			{
				_min = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("Min"));
			}
		}

		[
		Browsable(true),
		Description("Determines the maximum value for a int, long, float value."),
		Category("Data"),
		DefaultValue(_def_max),
		TypeConverter(typeof(RangeMaxConverter)),
		]
		public double Max
		{
			get
			{
				if (this.ComputedColumn) return _def_max;
				else return _max;
			}
			set
			{
				_max = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("Max"));
			}
		}

		[
		Browsable(true),
		Description("Determines if this field can be used in searches. When true, a custom method will be generated in the API to select by this field."),
		Category("Data"),
		DefaultValue(_def_isSearchable),
		]
		public bool IsSearchable
		{
			get { return _isSearchable; }
			set
			{
				_isSearchable = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("IsSearchable"));
			}
		}

		[
		Browsable(true),
		Description("Determines if this field has a database index."),
		Category("Data"),
		DefaultValue(_def_isIndexed),
		]
		public bool IsIndexed
		{
			get { return _isIndexed || _isSearchable; }
			set
			{
				_isIndexed = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("IsIndexed"));
			}
		}

		[
		Browsable(true),
		Description("Determines if this field is marked as unique."),
		Category("Data"),
		DefaultValue(_def_isUnique),
		]
		public bool IsUnique
		{
			get
			{
				if (this.ComputedColumn) return false;
				else return _isUnique || _primaryKey;
			}
			set
			{
				_isUnique = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("IsUnique"));
			}
		}

		[
		Browsable(true),
		Description("Determines the field collation."),
		Category("Data"),
		DefaultValue(""),
		]
		public string Collate
		{
			get
			{
				if (this.ComputedColumn) return "";
				else return _collate;
			}
			set
			{
				_collate = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("Collate"));
			}
		}

		[Browsable(false)]
		public Reference ParentTableRef
		{
			get { return _parentTableRef; }
			set { _parentTableRef = value; }
		}

		/// <summary>
		/// Determines the header text shown to users, if this column is visible in data grid displays.
		/// </summary>
		[
		Browsable(false),
		Description("Determines the header text shown to users, if this column is visible in data grid displays."),
		Category("Appearance"),
		]
		public string FriendlyName
		{
			get { return _friendlyName; }
			set
			{
				_friendlyName = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("friendlyName"));
			}
		}

		/// <summary>
		/// Determines the sort order of this field in relation to other data visible fields.
		/// </summary>
		[
		  Browsable(false),
		Description("Determines the sort order of this field in relation to other data visible fields."),
		Category("Appearance"),
		DefaultValue(_def_sortOrder),
		]
		public int SortOrder
		{
			get { return _sortOrder; }
			set
			{
				_sortOrder = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("sortOrder"));
			}
		}

		/// <summary>
		/// Determines if the column is visible in grids.
		/// </summary>
		[
		  Browsable(false),
		Description("Determines if the column is visible in grids."),
		Category("Appearance"),
		DefaultValue(_def_gridVisible),
		]
		public bool GridVisible
		{
			get { return _gridVisible; }
			set
			{
				_gridVisible = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("gridVisible"));
			}
		}

		/// <summary>
		/// Identifies the mask for data input.
		/// </summary>
		[
		Browsable(false),
		Description("Identifies the mask for data input and presentation."),
		Category("Mask"),
		DefaultValue(_def_mask),
		]
		public string Mask
		{
			get { return _mask; }
			set
			{
				_mask = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("mask"));
			}
		}

		[Browsable(false)]
		public override string CorePropertiesHash
		{
			get
			{
				string prehash =
					this.Name + "|" +					
					this.Identity + "|" +
					this.AllowNull + "|" +
					this.Default + "|" +
					this.Length + "|" +
					this.Collate + "|" +
					this.PrimaryKey + "|" +
					this.DataType.ToString();
				//return HashHelper.Hash(prehash);
				return prehash;
			}
		}

		[Browsable(false)]
		public override string CorePropertiesHashNoPK
		{
			get
			{
				string prehash = this.Name + "|" +
					this.Identity + "|" +
					this.AllowNull + "|" +
					this.Default + "|" +
					this.Length + "|" +
					this.Collate + "|" +
					this.DataType.ToString();
				//return HashHelper.Hash(prehash);
				return prehash;
			}
		}

		#endregion

		#region Methods

		#endregion

		#region IXMLable Members

		public override void XmlAppend(XmlNode node)
		{
			try
			{
				XmlDocument oDoc = node.OwnerDocument;

				XmlHelper.AddAttribute(node, "key", this.Key);
				XmlHelper.AddAttribute(node, "primaryKey", this.PrimaryKey);
				XmlHelper.AddAttribute(node, "computedColumn", this.ComputedColumn);
				XmlHelper.AddAttribute(node, "formula", this.Formula);
				XmlHelper.AddAttribute(node, "generated", this.Generated.ToString());
				XmlHelper.AddAttribute(node, "identity", (int)this.Identity);
				XmlHelper.AddAttribute(node, "name", this.Name);
				XmlHelper.AddAttribute(node, "codeFacade", this.CodeFacade);
				XmlHelper.AddAttribute(node, "description", this.Description);
				XmlHelper.AddAttribute(node, "dataFieldFriendlyName", this.FriendlyName);
				XmlHelper.AddAttribute(node, "dataFieldVisibility", this.GridVisible);
				XmlHelper.AddAttribute(node, "dataFieldSortOrder", this.SortOrder);
				XmlHelper.AddAttribute(node, "default", this.Default);
				XmlHelper.AddAttribute(node, "length", this.Length);
				XmlHelper.AddAttribute(node, "scale", this.Scale);
				XmlHelper.AddAttribute(node, "isIndexed", this.IsIndexed);
				XmlHelper.AddAttribute(node, "isUnique", this.IsUnique);
				XmlHelper.AddAttribute(node, "collate", this.Collate);
				XmlHelper.AddAttribute(node, "id", this.Id);
				XmlHelper.AddAttribute(node, "type", (int)this.DataType);
				XmlHelper.AddAttribute(node, "allowNull", this.AllowNull);
				if (this.Min != double.NaN) XmlHelper.AddAttribute(node, "min", this.Min);
				if (this.Max != double.NaN) XmlHelper.AddAttribute(node, "max", this.Max);
				XmlHelper.AddAttribute(node, "isSearchable", this.IsSearchable);
				XmlHelper.AddAttribute(node, "mask", this.Mask);
				XmlHelper.AddAttribute(node, "createdDate", _createdDate.ToString("yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture));

				if (RelationshipRef != null)
				{
					XmlNode relationshipRefNode = oDoc.CreateElement("relationshipRef");
					RelationshipRef.XmlAppend(relationshipRefNode);
					node.AppendChild(relationshipRefNode);
				}

				XmlNode parentTableRefNode = oDoc.CreateElement("parentTableRef");
				ParentTableRef.XmlAppend(parentTableRefNode);
				node.AppendChild(parentTableRefNode);

			}
			catch (Exception ex)
			{
				throw;
			}
		}

		public override void XmlLoad(XmlNode node)
		{
			try
			{
				_key = XmlHelper.GetAttributeValue(node, "key", _key);
				this.PrimaryKey = XmlHelper.GetAttributeValue(node, "primaryKey", _primaryKey);
				this.ComputedColumn = XmlHelper.GetAttributeValue(node, "computedColumn", _computedColumn);
				this.Formula = XmlHelper.GetAttributeValue(node,"formula", _formula);
				this.Generated = XmlHelper.GetAttributeValue(node, "generated", _def_generated);
				this.Identity = (IdentityTypeConstants)XmlHelper.GetAttributeValue(node, "identity", (int)_identity);
				this.Name = XmlHelper.GetAttributeValue(node, "name", _name);
				this.CodeFacade = XmlHelper.GetAttributeValue(node, "codeFacade", _codeFacade);
				this.Description = XmlHelper.GetAttributeValue(node, "description", _description);
				this.FriendlyName = XmlHelper.GetAttributeValue(node, "dataFieldFriendlyName", this.FriendlyName);
				this.GridVisible = XmlHelper.GetAttributeValue(node, "dataFieldVisibility", this.GridVisible);
				this.SortOrder = XmlHelper.GetAttributeValue(node, "dataFieldSortOrder", this.SortOrder);
				this.Min = XmlHelper.GetAttributeValue(node, "min", double.NaN);
				this.Max = XmlHelper.GetAttributeValue(node, "max", double.NaN);
				this.Mask = XmlHelper.GetAttributeValue(node, "mask", _def_mask);
				_createdDate = DateTime.ParseExact(XmlHelper.GetAttributeValue(node, "createdDate", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture)), "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
				this.IsSearchable = XmlHelper.GetAttributeValue(node, "isSearchable", this.IsSearchable);
				this.IsIndexed = XmlHelper.GetAttributeValue(node, "isIndexed", this.IsIndexed);
				this.IsUnique = XmlHelper.GetAttributeValue(node, "isUnique", this.IsUnique);
				this.Collate = XmlHelper.GetAttributeValue(node, "collate", this.Collate);
				XmlNode relationshipRefNode = node.SelectSingleNode("relationshipRef");
				if (relationshipRefNode != null)
				{
					RelationshipRef = new Reference(this.Root);
					RelationshipRef.XmlLoad(relationshipRefNode);
				}

				string defaultValue = node.Attributes["default"].Value;
				defaultValue = defaultValue.Replace("\n", "");
				defaultValue = defaultValue.Replace("\r", "");
				this.Default = defaultValue;

				this.Length = XmlHelper.GetAttributeValue(node, "length", _length);
				this.Scale = XmlHelper.GetAttributeValue(node, "scale", _scale);
				this.SetId(XmlHelper.GetAttributeValue(node, "id", _id));

				XmlNode parentTableRefNode = node.SelectSingleNode("parentTableRef");
				ParentTableRef = new Reference(this.Root);
				ParentTableRef.XmlLoad(parentTableRefNode);

				string typeString = node.Attributes["type"].Value;
				if (typeString != "")
				{
					//DO NOT set the real field as there is validation on it.
					_type = (System.Data.SqlDbType)int.Parse(typeString);
				}

				this.AllowNull = XmlHelper.GetAttributeValue(node, "allowNull", _allowNull);

				this.Dirty = false;
			}
			catch (Exception ex)
			{
				throw;
			}

		}

		#endregion

		#region Helpers

		public override Reference CreateRef()
		{
			Reference returnVal = new Reference(this.Root);
			returnVal.Ref = this.Id;
			returnVal.RefType = ReferenceType.Column;
			return returnVal;
		}

		[Browsable(false)]
		public override string PascalName
		{
			get
			{
				if ((this.CodeFacade != "") && (((ModelRoot)this.Root).TransformNames))
					return StringHelper.DatabaseNameToPascalCase(this.CodeFacade);
				else if ((this.CodeFacade == "") && (((ModelRoot)this.Root).TransformNames))
					return StringHelper.DatabaseNameToPascalCase(this.Name);
				if ((this.CodeFacade != "") && !(((ModelRoot)this.Root).TransformNames))
					return this.CodeFacade;
				else if ((this.CodeFacade == "") && !(((ModelRoot)this.Root).TransformNames))
					return this.Name;
				return this.Name; //Default
			}
		}

		[Browsable(false)]
		public string EnumType
		{
			get { return _enumType; }
			set { _enumType = value; }
		}

		public override string GetCodeType(bool allowNullable, bool forceNull)
		{
			string retval = "";
			if (this.EnumType != "")
			{
				retval = this.EnumType;
				if (allowNullable && (this.AllowNull || forceNull))
					retval += "?";
				return retval;
			}
			else
			{
				return base.GetCodeType(allowNullable, forceNull);
			}
		}

		#endregion

		#region ICodeFacadeObject Members

		[
		Browsable(true),
		Description("Determines the object name used in the API. If this property is blank the 'Name' property is used in the API. This property can be used to mask the database identifier."),
		Category("Design"),
		DefaultValue(""),
		]
		public string CodeFacade
		{
			get { return _codeFacade; }
			set
			{
				_codeFacade = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("codeFacade"));
			}
		}

		public string GetCodeFacade()
		{
			if (this.CodeFacade == "")
				return this.Name;
			else
				return this.CodeFacade;
		}

		#endregion

	}
}